package knowledge;

/**
 * cpu共享机制测试代码
 * 这个代码主要是展示了伪共享(https://tech.meituan.com/2016/11/18/disruptor.html)导致的性能问题
 * 代码的主要思路是访问两个类ValuePadding和ValueNoPadding的value, 其中ValuePadding是进行了元素填充使得每次访问value都是在不同发缓存行上.
 * 这样当多个线程去更改value的值时,不必每个线程每修改一次value都重新去内存取数据.
 * 当只有一个thread时,运行时间几乎没有区别. 但是两个及以上线程运行代码时,时间差距立马显示出来了
 * @author LiYilin on 2020/11/19
 */
public class FalseSharing implements Runnable {

    public final static long ITERATOR = 500L * 1000L * 100L;
    private int arrayIndex = 0;

    private static ValuePadding[] longs;
//    private static ValueNoPadding[] longs;

    public FalseSharing(final int arrayIndex) {
        this.arrayIndex = arrayIndex;
    }


    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            System.gc();
            final long start = System.currentTimeMillis();
            runTest(i);
            System.out.println("Thread num" + i + " duration = " + (System.currentTimeMillis() - start));
        }
    }

    private static void runTest(int numThreads) throws InterruptedException {
        Thread[] threads = new Thread[numThreads];
        longs = new ValuePadding[numThreads];
//        longs = new ValueNoPadding[numThreads];
        for (int i = 0; i < longs.length; i++) {
            longs[i] = new ValuePadding();
//            longs[i] = new ValueNoPadding();
        }

        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new FalseSharing(i));
        }

        for (Thread thread : threads) {
            thread.start();
        }

        for (Thread thread : threads) {
            thread.join();
        }
    }

    /**
     * 这个run方法是关键
     * 当单个线程访问时,不存在被别的线程修改. 因此每次都是从L1缓存中获取value值
     * 当两个及以上线程同时修改value时,缓存失效,只能每次都去内存中读取value的值
     * 从运行结果看, 只有一个线程时, 运行时间几乎没有差别373ms 370ms
     * 两个线程同时访问时,耗时差距就急剧拉大: 354ms 1078ms
     */
    @Override
    public void run() {
        long i = ITERATOR + 1;
        while (0 != --i) {
            longs[arrayIndex].value = 0L;
        }
    }

    public final static class ValuePadding{
        protected long p1, p2, p3, p4, p5, p6, p7;
        protected volatile long value = 0L;
        protected long p9, p10, p11, p12, p13, p14;
        protected long p15;
    }

    public final static class ValueNoPadding{
        protected volatile long value = 0L;
    }
}
